home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Adobe Graphics & Publishing SDK 1996 December
/
Adobe Graphics & Publishing SDK 1996 December.iso
/
mac
/
Premiere 4.2 SDK r3 Mac
/
Examples
/
Projects
/
Burn-Time-Code
/
Burn-Time-Code.c
< prev
next >
Wrap
Text File
|
1996-01-25
|
21KB
|
651 lines
//========================================================================================
//
// Burn Time Code.c - Burn Time Code on to a frame in Adobe Premiere¬.
//
// Written by Bryan K. "Beaker" Ressler.
//
// Copyright ⌐ 1993-96, Adobe Systems Incorporated, all rights reserved worldwide.
//
// Version 1.00 10/20/93 Original version.
// Version 1.01 9/12/94 Updated for 4.0.
// Version 1.02 10/6/95 Updated for 4.2 and CW7.
//
//========================================================================================
//========================================================================================
// Includes - use precompiled headers if compiling with CodeWarrior.
//========================================================================================
#ifdef __MWERKS__
#ifdef powerc
#include "PremierePPC"
#else
#include "Premiere68k"
#endif
#else
#include "Premiere.h"
#endif
//========================================================================================
// Resource IDs
//========================================================================================
#define kDialog 100 // DLOG - Our setings dialog
#define kStrings 100 // STR# - Our string list
//========================================================================================
// Strings in our string list
//========================================================================================
#define kBkgdPrompt 1 // Color picker prompt for background color
#define kTextPrompt 2 // Color picker prompt for text color
//========================================================================================
// Settings dialog items
//========================================================================================
enum {
riOK = 1,
riCancel,
riSample,
riStartTimeText,
riStartTimeInterp, // 5
riDropFrameCheck,
riPositionPopup,
riBkgdSwatch,
riFontPopup,
riBoldCheck, // 10
riItalicCheck,
riTextSwatch
};
enum { // Time code positions
posLeft = 1,
posCenter,
posRight
};
//========================================================================================
// Private types
//========================================================================================
typedef struct {
short version; // BCD record version number (e.g. 0x0120 = 1.2)
long startTime; // Start time in frames
Str255 fontName; // The font for the time code display
RGBColor bkgdColor; // The background color
RGBColor textColor; // The text color
short position; // Time code position (left/center/right)
Boolean isBold; // Bold text?
Boolean isItalic; // Italic text?
Boolean dropFrame; // Drop-frame time code?
} BTCSpecRec, *BTCSpecPtr, **BTCSpecHdl;
typedef struct {
BTCSpecRec btcSpec;
VideoHandle theData;
} LocalRec, **LocalHand;
//========================================================================================
// Constants
//========================================================================================
#define kSpecsVersion 0x0120 // Version of our specs record (1.2)
//========================================================================================
// Static prototypes
//========================================================================================
static void InitBTCSpec(BTCSpecPtr bs);
static short BTCSetup(VideoHandle theData);
static void SwapBTCColors(DialogPtr dialog, BTCSpecPtr bs);
static Boolean OptionDown(void);
static pascal Boolean BTCFilter(DialogPtr dialog, EventRecord *event, short *hit);
static short FindMenuItem(MenuHandle menu, StringPtr name);
static short SetupFontMenu(BTCSpecPtr bs, MenuHandle menu);
static pascal void SwatchItem(DialogPtr dialog, short item);
static pascal void SampleItem(DialogPtr dialog, short item);
static void ImageProc(BTCSpecPtr bs, GWorldPtr src, GWorldPtr target, Rect *overlaySize,
short fps, long part, short sizeFlags);
static PicHandle MakeOverlayPicture(BTCSpecPtr bs, short width, short height, short fps,
long part, short sizeFlags);
static short SetupText(BTCSpecPtr bs, Rect *box);
static void DrawTimeCode(BTCSpecPtr bs, Rect *box, short textSize, short fps, long part);
//========================================================================================
// Dispatcher for filter functions.
//========================================================================================
pascal short main (short selector, VideoHandle theData)
{
BTCSpecHdl camHdl;
CGrafPtr oldWorld;
GDHandle oldDevice;
short fps, result = 0;
switch (selector) {
case fsExecute:
camHdl = (BTCSpecHdl)(*theData)->specsHandle;
if (camHdl != nil) {
GetGWorld(&oldWorld, &oldDevice);
SetGWorld((*theData)->destination, nil);
HLock((Handle)camHdl);
fps = (*theData)->fps;
if ((*camHdl)->dropFrame)
fps |= 0x8000;
ImageProc(*camHdl, (*theData)->source, (*theData)->destination, nil, fps,
(*theData)->part, (*theData)->sizeFlags);
HUnlock((Handle)camHdl);
ForeColor(blackColor);
BackColor(whiteColor);
SetGWorld(oldWorld, oldDevice);
} else result = 1;
break;
case fsSetup:
result = BTCSetup(theData);
break;
}
return(result);
}
//========================================================================================
// Initialize a burn time code specification record with reasonable default values. If bs
// is in a Handle, it should be locked before calling this routine, because we call
// GetFontName, which moves memory.
//========================================================================================
static void InitBTCSpec(BTCSpecPtr bs)
{
RGBColor white = { 0xffff, 0xffff, 0xffff };
RGBColor black = { 0x0000, 0x0000, 0x0000 };
bs->version = kSpecsVersion;
bs->startTime = 0;
GetFontName(applFont, bs->fontName);
bs->bkgdColor = black;
bs->textColor = white;
bs->position = posCenter;
bs->isBold = false;
bs->isItalic = false;
bs->dropFrame = false;
}
//========================================================================================
// Conduct the Burn Time Code filter's setup dialog.
//========================================================================================
static short BTCSetup(VideoHandle theData)
{
Str63 prompt, timeStr;
RGBColor oldColor, newColor;
LocalHand ldata;
MenuHandle menu;
DialogPtr dialog;
GrafPtr oldPort;
BTCSpecPtr bs;
long temp;
short item, result = 0;
short origfps, fps;
unsigned char blank = 0;
Boolean redrawSample = false;
InitCursor();
GetPort(&oldPort);
ldata = (LocalHand)NewHandleClear(sizeof(LocalRec));
if (ldata == nil)
return(1);
// Get our spec if one was provided, otherwise initialize with defaults
HLock((Handle)ldata);
(*ldata)->theData = theData;
bs = &(*ldata)->btcSpec;
origfps = fps = (*theData)->fps;
if (bs->dropFrame)
fps |= 0x8000;
if ((*theData)->specsHandle) {
BlockMove(*(*theData)->specsHandle, (Ptr)bs, sizeof(BTCSpecRec));
if (bs->version != kSpecsVersion)
InitBTCSpec(bs);
} else InitBTCSpec(bs);
// Get the dialog
dialog = GetNewDialog(kDialog, nil, (WindowPtr)-1);
SetPort(dialog);
CenterWindow(dialog, &(*GetMainDevice())->gdRect);
SetWRefCon(dialog,(long)ldata);
// Set up the user items
UserItem(dialog, riSample, SampleItem);
UserItem(dialog, riBkgdSwatch, SwatchItem);
UserItem(dialog, riTextSwatch, SwatchItem);
// Set control values
SetCValue(dialog, riBoldCheck, bs->isBold);
SetCValue(dialog, riItalicCheck, bs->isItalic);
SetCValue(dialog, riPositionPopup, bs->position);
SetCValue(dialog, riDropFrameCheck, bs->dropFrame);
// Set edit item text
Time2Str(bs->startTime, timeStr, fps, tsHours);
SetEText(dialog, riStartTimeText, timeStr);
ParamText(timeStr, &blank, &blank, &blank);
SelIText(dialog, riStartTimeText, 0, 32767);
// Set up the font menu
menu = (MenuHandle)GetCRef(dialog, riFontPopup);
item = SetupFontMenu(bs, menu);
SetCValue(dialog, riFontPopup, item);
WidenMenu(dialog, riFontPopup);
// Pose the dialog
ShowModal(dialog);
do {
item = 0;
if (redrawSample) {
redrawSample = false;
SampleItem(dialog, riSample);
}
PrModalDialog(BTCFilter, &item);
switch (item) {
case riOK:
// Store our camcorder filter specification
if ((*theData)->specsHandle) {
SafeSetHandleSize((*theData)->specsHandle, sizeof(BTCSpecRec));
BlockMove((Ptr)bs, *(*theData)->specsHandle,
sizeof(BTCSpecRec));
} else {
(*theData)->specsHandle = NewHandle(sizeof(BTCSpecRec));
if ((*theData)->specsHandle)
BlockMove((Ptr)bs, *(*theData)->specsHandle,
sizeof(BTCSpecRec));
}
break;
case riCancel:
item = riOK;
result = 1;
break;
case riStartTimeText:
GetEText(dialog, item, timeStr);
fps = origfps;
if (bs->dropFrame)
fps |= 0x8000;
bs->startTime = Str2Time(timeStr + 1, *timeStr, fps);
Time2Str(bs->startTime, timeStr, fps, tsHours);
ParamText(timeStr, &blank, &blank, &blank);
InvalItem(dialog, riStartTimeInterp);
redrawSample = true;
break;
case riDropFrameCheck:
bs->dropFrame = !bs->dropFrame;
SetCValue(dialog, item, bs->dropFrame);
redrawSample = true;
break;
case riPositionPopup:
bs->position = GetCValue(dialog, item);
redrawSample = true;
break;
case riBkgdSwatch:
if (OptionDown())
SwapBTCColors(dialog, bs);
else {
GetIndString(prompt, kStrings, kBkgdPrompt);
oldColor = bs->bkgdColor;
if (GeneralGetColor(prompt, &oldColor, &newColor)) {
bs->bkgdColor = newColor;
SwatchItem(dialog, item);
}
SetPort(dialog);
ParamText(timeStr, &blank, &blank, &blank);
}
redrawSample = true;
break;
case riFontPopup:
temp = GetCValue(dialog, item);
menu = (MenuHandle)GetCRef(dialog, riFontPopup);
GetItem(menu, temp, bs->fontName);
redrawSample = true;
break;
case riBoldCheck:
bs->isBold = !bs->isBold;
SetCValue(dialog, item, bs->isBold);
redrawSample = true;
break;
case riItalicCheck:
bs->isItalic = !bs->isItalic;
SetCValue(dialog, item, bs->isItalic);
redrawSample = true;
break;
case riTextSwatch:
if (OptionDown())
SwapBTCColors(dialog, bs);
else {
GetIndString(prompt, kStrings, kTextPrompt);
oldColor = bs->textColor;
if (GeneralGetColor(prompt, &oldColor, &newColor)) {
bs->textColor = newColor;
SwatchItem(dialog, item);
}
SetPort(dialog);
ParamText(timeStr, &blank, &blank, &blank);
}
redrawSample = true;
break;
default:
break;
}
} while (item != riOK);
// Clean up
HUnlock((Handle)ldata);
DisposeModal(dialog);
DisposeHandle((Handle)ldata);
SetPort(oldPort);
return(result);
}
//========================================================================================
// Swaps the background and text colors
//========================================================================================
static void SwapBTCColors(DialogPtr dialog, BTCSpecPtr bs)
{
RGBColor temp;
temp = bs->bkgdColor;
bs->bkgdColor = bs->textColor;
bs->textColor = temp;
SwatchItem(dialog, riBkgdSwatch);
SwatchItem(dialog, riTextSwatch);
}
//========================================================================================
// Returns true if the Option key is currently down.
//========================================================================================
static Boolean OptionDown(void)
{
return((GetModifiers(nil) & bOption) != 0);
}
//========================================================================================
// Dialog filter for the Burn Time Code setup dialog. It does the hits on popup control
// items.
//========================================================================================
static pascal Boolean BTCFilter(DialogPtr dialog, EventRecord *event, short *hit)
{
Point where;
ControlHandle thecontrol;
short which, oldval, part;
Boolean result = false;
result = modalfilter(dialog, event, hit);
if (!result && event->what == mouseDown) {
where = event->where;
GlobalToLocal(&where);
if (which = FindDItem(dialog, where) + 1) {
if (FindControl(where, dialog, &thecontrol)) {
oldval = GetCtlValue(thecontrol);
part = TrackControl(thecontrol, where, nil);
if (oldval != GetCtlValue(thecontrol)) {
*hit = which;
result = true;
} else if (part) {
*hit = which;
result = true;
} else {
*hit = 0;
result = true;
}
}
}
}
return(result);
}
//========================================================================================
// Given a menu and an item name, return the item number or 0 if not found.
//========================================================================================
static short FindMenuItem(MenuHandle menu, StringPtr name)
{
short numItems, item = 0, i = 0;
Str255 str;
numItems = CountMItems(menu);
for (i = 1; i <= numItems; i++) {
GetItem(menu, i, str);
if (StringsSame(str, name))
item = i;
}
return item;
}
//========================================================================================
// Deletes any existing item from the font menu, appends all installed font names, then
// tries to find the current font in the menu, returning its item number if found or 0 if
// not found.
//========================================================================================
static short SetupFontMenu(BTCSpecPtr bs, MenuHandle menu)
{
short i;
// First, delete all existing items from the menu
i = CountMItems(menu);
do DelMenuItem(menu, i--); while (i > 0);
// Add the fonts
AddResMenu(menu, 'FONT');
// Look for the current font in the menu
i = FindMenuItem(menu, bs->fontName);
return(i);
}
//========================================================================================
// Draw the color swatches (both handled by this user item proc).
//========================================================================================
static pascal void SwatchItem(DialogPtr dialog, short item)
{
Rect box;
RGBColor color;
LocalHand ldata;
ldata = (LocalHand)GetWRefCon(dialog);
switch (item) {
case riBkgdSwatch:
color = (*ldata)->btcSpec.bkgdColor;
break;
case riTextSwatch:
color = (*ldata)->btcSpec.textColor;
break;
}
GetDRect(dialog, item, &box);
FrameRect(&box);
InsetRect(&box, 1, 1);
DitherBox(dialog, &box, &color);
SetGray(0);
}
//========================================================================================
// Draw the sample in the setup dialog.
//========================================================================================
static pascal void SampleItem(DialogPtr dialog, short item)
{
Rect all, box;
LocalHand ldata;
VideoHandle theData;
GWorldPtr src, dest;
Rect overlaySize = { 0, 0, 480, 640 }; // Render at 640 x 480 for preview
short fps;
ldata = (LocalHand)GetWRefCon(dialog);
theData = (*ldata)->theData;
src = (*theData)->source;
dest = (*theData)->destination;
box = src->portRect;
GetDRect(dialog, item, &all);
FrameRect(&all);
InsetRect(&all, 1, 1);
// ldata is already locked here (by the dialog routine)
fps = (*theData)->fps;
if ((*ldata)->btcSpec.dropFrame)
fps |= 0x8000;
ImageProc(&(*ldata)->btcSpec, src, dest, &overlaySize, fps , 0, (*theData)->sizeFlags);
CopyBits((BitMap *)&dest->portPixMap, &dialog->portBits, &box, &all, ditherCopy,
dialog->visRgn);
}
//========================================================================================
// Apply our filter between the src and dest worlds. The overlaySize rectangle allows the
// caller to force the picture to be rendered at a given size. This allows the SampleItem
// proc to image the overlay picture at the output options size, so the scaled down over-
// lay will be more accurate. Note that the image is still overlayed at the size
// specified. FPS SHOULD ALREADY HAVE THE HIGH BIT SET IF DROP FRAME IS TURNED ON!
//========================================================================================
static void ImageProc(BTCSpecPtr bs, GWorldPtr src, GWorldPtr target, Rect *overlaySize,
short fps, long part, short sizeFlags)
{
Rect box;
GDHandle oldGD;
PicHandle overlay;
GWorldPtr oldGW;
short hSize, vSize;
// Point at target
GetGWorld(&oldGW, &oldGD);
SetGWorld(target, nil);
// Copy the frame into the target
box = src->portRect;
CopyBits((BitMap*)&src->portPixMap, (BitMap*)&target->portPixMap, &box, &box,
srcCopy, nil);
// Create the camcorder overlay picture and draw it over the destination port.
if (overlaySize != nil) {
hSize = overlaySize->right - overlaySize->left;
vSize = overlaySize->bottom - overlaySize->top;
} else {
hSize = box.right - box.left;
vSize = box.bottom - box.top;
}
overlay = MakeOverlayPicture(bs, hSize, vSize, fps, part, sizeFlags);
if (overlay)
DrawPicture(overlay, &box);
KillPicture(overlay);
// Restore
ForeColor(blackColor); PenNormal();
SetGWorld(oldGW, oldGD);
}
//========================================================================================
// The supervisor routine that constructs the timecode overlay picture. Returns nil if
// there wasn't enough memory to build the picture.
//========================================================================================
static PicHandle MakeOverlayPicture(BTCSpecPtr bs, short width, short height, short fps,
long part, short sizeFlags)
{
Rect box;
short textSize;
PicHandle pict;
// Build the picture bounds and compensate for fields, half-vertical, and half-horiz-
// ontal flags.
SetRect(&box, 0, 0, width, height);
if (sizeFlags & gvHalfH)
box.right *= 2;
if (sizeFlags & (gvHalfV + gvFieldsEven + gvFieldsOdd))
box.bottom *= 2;
if (sizeFlags & (gvFieldsEven + gvFieldsOdd))
part /= 2;
// Build the overlay picture
pict = OpenPicture(&box);
// Start clean
ForeColor(blackColor);
PenNormal();
// Set up text parameters
textSize = SetupText(bs, &box);
// Burn the timecode
DrawTimeCode(bs, &box, textSize, fps, part);
// End clean
ForeColor(blackColor);
PenNormal();
ClosePicture();
return(pict);
}
//========================================================================================
// Set up the current port with the appropriate font, size, style, and mode.
//========================================================================================
static short SetupText(BTCSpecPtr bs, Rect *box)
{
short fNum, size;
Style style;
// Get the information
GetFNum(bs->fontName, &fNum);
size = (box->right - box->left) / 20;
if (size < 12) size = 12;
style = normal;
if (bs->isBold)
style |= bold;
if (bs->isItalic)
style |= italic;
// Apply it
TextFont(fNum);
TextFace(style);
TextMode(srcOr);
TextSize(size);
return(size);
}
//========================================================================================
// Draw the "burned-in" timecode.
//========================================================================================
static void DrawTimeCode(BTCSpecPtr bs, Rect *box, short textSize, short fps, long part)
{
Str63 timeStr;
FontInfo info;
Rect tcBox;
RGBColor fiftyPercentGray = { 0x7fff, 0x7fff, 0x7fff };
short width, height, tcWidth, tcGutter;
// Build the timecode string and get its width
Time2Str(bs->startTime + part, timeStr, fps, tsHours);
tcWidth = StringWidth(timeStr);
tcGutter = tcWidth * 4 / 100; // 4% gutters on each side
// Build the box in which the timecode string will be drawn
tcBox = *box;
width = tcBox.right - tcBox.left;
height = tcBox.bottom - tcBox.top;
InsetRect(&tcBox, width * 12 / 100, height * 12 / 100); // 12% - title-safe
tcBox.top = tcBox.bottom - (textSize * 120 / 100); // 120% of the point size
switch (bs->position) {
case posLeft:
tcBox.right = tcBox.left + tcGutter + tcWidth + tcGutter;
break;
case posCenter:
tcWidth = tcWidth + tcGutter * 2;
tcBox.left += ((tcBox.right - tcBox.left) - tcWidth) / 2;
tcBox.right = tcBox.left + tcWidth;
break;
case posRight:
tcBox.left = tcBox.right - tcGutter - tcWidth - tcGutter;
break;
}
// Draw the background
OpColor(&fiftyPercentGray);
RGBForeColor(&bs->bkgdColor);
PenMode(blend);
PaintRect(&tcBox);
// Draw the timecode
PenNormal();
GetFontInfo(&info);
RGBForeColor(&bs->textColor);
MoveTo(tcBox.left + tcGutter, tcBox.bottom - info.descent);
DrawString(timeStr);
}